package com.vanguard.codec;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Title: 客户端解码器 解码方式为 自定义结束符
 * @Description: 解码Server的响应报文给Client
 * @Author: vanguard
 * @Version: 1.0
 * @Date: 2018/12/28
 */
public class ClientUserDefinedTailedDecode extends CumulativeProtocolDecoder {
    private final String charset;
    private final String tailed = ";|";
    private static Logger logger = LoggerFactory.getLogger(ClientUserDefinedTailedDecode.class);
    
    //注意这里使用了Mina自带的AttributeKey类来定义保存在IoSession中对象的键值,其可有效防止键值重复  
    private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
    
    /**
     * @param charset 解码编码
     */
    public ClientUserDefinedTailedDecode(String charset){
        this.charset = charset;
    }
    
    private Context getContext(IoSession session){
        Context context = (Context)session.getAttribute(CONTEXT);  
        if(null == context){  
            context = new Context();  
            session.setAttribute(CONTEXT, context);  
        }  
        return context;  
    }  
    @Override  
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        
        Context ctx = this.getContext(session);  
        IoBuffer buffer = ctx.innerBuffer;

        byte[] tailBytes = tailed.getBytes(charset);
        int tailBytesLength = tailBytes.length;
        
        byte[] tailMatchBytes = ctx.getTailMatchBytes();
        int tailMatchBytesContentNum = ctx.getTailMatchBytesContentNum();
        int tailMatchBytesHeadIndex = ctx.getTailMatchBytesHeadIndex();
        if(null == tailMatchBytes){
        	tailMatchBytes = new byte[tailBytesLength];
        }
        
        logger.info("in dodecode tailed:" + tailed);

		//判断position和limit之间是否有元素
        while(in.hasRemaining()){
        	byte b = in.get();
        	//是否结束
        	boolean isOver = false;
        	//判断是否 需要 先填满 结尾 缓存
        	if(tailMatchBytesContentNum < tailBytesLength){
        		tailMatchBytes[tailMatchBytesHeadIndex] = b;
        		tailMatchBytesHeadIndex++;
        		tailMatchBytesContentNum++;
        		//首次填满缓存
        		if(tailMatchBytesContentNum == tailBytesLength){
        			tailMatchBytesHeadIndex = 0;
        			//判断当前内容是否为结束符
        			isOver = isMatchTail(tailBytesLength, tailMatchBytesHeadIndex, 
            				tailMatchBytes, tailBytes);
        			if(isOver){
        				out.write("");
                        //重置上下文
                        ctx.reset();
                        return true;
        			}
        		}
        		
        	} else {
        		byte tailMatchBytesHead = tailMatchBytes[tailMatchBytesHeadIndex];
    			tailMatchBytes[tailMatchBytesHeadIndex] = b;
    			tailMatchBytesHeadIndex = (tailMatchBytesHeadIndex + 1)%tailBytesLength;
    			
        		buffer.put(tailMatchBytesHead);
        		
        		isOver = isMatchTail(tailBytesLength, tailMatchBytesHeadIndex, 
        				tailMatchBytes, tailBytes);
        		
        		if(isOver) {
        			buffer.flip();
        			
        			//输出解码内容
        			byte[] message = new byte[buffer.limit()];
                    buffer.get(message);  
                    out.write(new String(message, charset));
                    //重置上下文
                    ctx.reset();
                    return true;
    			}
			}
        	
        	//往context中存值，应对粘包
        	ctx.setTailMatchBytes(tailMatchBytes);
        	ctx.setTailMatchBytesContentNum(tailMatchBytesContentNum);
        	ctx.setTailMatchBytesHeadIndex(tailMatchBytesHeadIndex);
        }
        
        return false;
    }
    
    private boolean isMatchTail(int tailBytesLength, int tailMatchBytesHeadIndex, 
    		byte[] tailMatchBytes, byte[] tailBytes){
    	boolean isOver = true;
    	for(int i = 0;i<tailBytesLength;i++){
			int tailMatchBytesIndex = (tailMatchBytesHeadIndex+i)%tailBytesLength;
			//如果与结尾不匹配
			if(tailBytes[i] != tailMatchBytes[tailMatchBytesIndex]){
				isOver = false;
				break;
			}
		}
    	return isOver;
    }
    
    private class Context{
		/**
		 * 用于累积数据的IoBuffer
		 */
		private final IoBuffer innerBuffer;

		/**
		 * 结束符的字节数组
		 */
		private byte[] tailMatchBytes = null;
		/**
		 * 查找自定义结束符的索引
		 */
        private int tailMatchBytesHeadIndex = 0;
		/**
		 * 查找自定义结束符的内容个数
		 */
		private int tailMatchBytesContentNum = 0;
        
		public Context(){
            innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
        }
 
        public byte[] getTailMatchBytes() {
			return tailMatchBytes;
		}
		public void setTailMatchBytes(byte[] tailMatchBytes) {
			this.tailMatchBytes = tailMatchBytes;
		}
		
		public int getTailMatchBytesHeadIndex() {
			return tailMatchBytesHeadIndex;
		}
		public void setTailMatchBytesHeadIndex(int tailMatchBytesHeadIndex) {
			this.tailMatchBytesHeadIndex = tailMatchBytesHeadIndex;
		}
		
		public int getTailMatchBytesContentNum() {
			return tailMatchBytesContentNum;
		}
		public void setTailMatchBytesContentNum(int tailMatchBytesContentNum) {
			this.tailMatchBytesContentNum = tailMatchBytesContentNum;
		}
        
        public void reset(){
            this.innerBuffer.clear(); //Set limit=capacity and position=0 and mark=-1  
            
            tailMatchBytes = null;
            tailMatchBytesHeadIndex = 0;
            tailMatchBytesContentNum = 0;
        }  
    }  
} 